package com.example.springbootsecurity.config;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.password.CompromisedPasswordChecker;
import org.springframework.security.authentication.password.CompromisedPasswordException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.password.HaveIBeenPwnedRestApiPasswordChecker;

import java.io.IOException;

/**
 * 配置Spring Security的类
 * 定义了项目的安全策略，包括请求的授权规则、登录配置和注销配置
 */
//@Configuration
//@EnableWebSecurity
public class SpringSecurityConfig {

    /**
     * 配置安全过滤器链，定义HTTP安全策略
     * 
     * @param http HttpSecurity对象，用于构建安全配置
     * @return SecurityFilterChain 安全过滤器链实例
     * @throws Exception 配置过程中可能抛出异常
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 配置请求授权和表单登录策略
        http
                .authorizeHttpRequests(authorize -> authorize
                        .anyRequest().authenticated()
                )
                .formLogin((login) -> login
                        .failureHandler(new CompromisedPasswordAuthenticationFailureHandler())
                );
        // 构建并返回配置好的SecurityFilterChain对象
        return http.build();
    }


    /**
     * 创建并返回一个CompromisedPasswordChecker实例，用于检查密码是否在已泄露密码数据库中
     * 
     * @return 返回一个CompromisedPasswordChecker接口的实现类实例
     */
    @Bean
    public CompromisedPasswordChecker compromisedPasswordChecker() {
        return new HaveIBeenPwnedRestApiPasswordChecker();
    }

    /**
     * 自定义身份验证失败处理程序
     * 处理登录失败的情况，特别是密码泄露的情况
     */
    static class CompromisedPasswordAuthenticationFailureHandler implements AuthenticationFailureHandler {

        private final SimpleUrlAuthenticationFailureHandler defaultFailureHandler = new SimpleUrlAuthenticationFailureHandler("/login?error");

        private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

        /**
         * 处理身份验证失败的情况。
         * 
         * @param request  发生身份验证失败的HTTP请求对象
         * @param response 发送响应的HTTP响应对象
         * @param exception 身份验证过程中抛出的异常
         * 
         * @throws IOException 如果在重定向时发生I/O错误
         * @throws ServletException 如果在处理请求时发生Servlet错误
         */
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                            AuthenticationException exception) throws IOException, ServletException {
            // 检查异常是否为密码泄露相关的异常
            if (exception instanceof CompromisedPasswordException) {
                // 如果是密码泄露异常，则重定向用户至密码重置页面
                this.redirectStrategy.sendRedirect(request, response, "/reset-password");
                return;
            }
            // 对于其他类型的认证失败，使用默认的失败处理器进行处理
            this.defaultFailureHandler.onAuthenticationFailure(request, response, exception);
        }

    }

}

